package pgui.visual;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;

import pgui.PVisual;

public class PComponent extends PDesigner {
	private Integer lastX, lastY, lastWidth, lastHeight;
	private Integer parentLastX, parentLastY;
	private PVisual visual = null;
	private PComponent parent = null;
	private ArrayList<PComponent> children = new ArrayList<PComponent>();

	// Creation
	public PComponent(PVisual visual) {
		this.setVisual(visual);
	}

	// Shortcuts
	public void setCursor(int type) {
		getVisual().setCursor(Cursor.getPredefinedCursor(type));
	}

	// Area Functions
	public boolean mouseInside(int x, int y) {
		if (this.getLastX() == null)
			return false;

		Integer lastX = this.getLastX();
		Integer lastY = this.getLastY();

		if (x > lastX && x < lastX + this.getLastWidth())
			if (y > lastY && y < lastY + this.getLastHeight())
				return true;
		return false;
	}

	// Paint Events
	public void onPaint(Graphics2D g) {
		// Last X & Y
		int lastX = this.getX();
		int lastY = this.getY();

		if (this.getParent() != null) {
			lastX += this.getRelativeX() * this.getParent().getLastWidth()
					/ 100;
			lastY += this.getRelativeY() * this.getParent().getLastHeight()
					/ 100;
		} else {
			lastX += this.getRelativeX() * this.getVisual().getWidth() / 100;
			lastY += this.getRelativeY() * this.getVisual().getHeight() / 100;
		}
		this.setLastX(lastX);
		this.setLastY(lastY);

		// Last Width & Height
		int lastWidth = this.getWidth();
		int lastHeight = this.getHeight();

		if (this.getParent() != null) {
			lastWidth += this.getRelativeWidth()
					* this.getParent().getLastWidth() / 100;
			lastHeight += this.getRelativeHeight()
					* this.getParent().getLastHeight() / 100;
		} else {
			lastWidth += this.getRelativeWidth() * this.getVisual().getWidth()
					/ 100;
			lastHeight += this.getRelativeHeight()
					* this.getVisual().getHeight() / 100;
		}

		this.setLastWidth(lastWidth);
		this.setLastHeight(lastHeight);

		// Draw
		g.translate(this.getLastX(), this.getLastY());

		if (visual.getClip()) {
			if (this.getClip()) {
				g.setClip(0, 0, this.getLastWidth(), this.getLastHeight());
			} else {
				AffineTransform startTransform = g.getTransform();
				g.translate(-startTransform.getTranslateX(),
						-startTransform.getTranslateY());

				g.setClip(0, 0, this.getVisual().getWidth(), this.getVisual()
						.getHeight());

				g.setTransform(startTransform);
			}
		}

		g.setColor(new Color(this.getRed(), this.getGreen(), this.getBlue()));
	}

	public void onPaintChild(Graphics2D g) {
		for (PComponent child : children) {
			AffineTransform startTransform = g.getTransform();

			g.setColor(new Color(child.getRed(), child.getGreen(), child
					.getBlue()));
			g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
					child.getOpacity() / 100.0f));

			child.setParentLastX(this.getLastX());
			child.setParentLastY(this.getLastY());

			child.onPaint(g);
			child.onPaintChild(g);
			g.setTransform(startTransform);
		}
	}

	// Child
	public boolean containChild(PComponent child) {
		if (this.getChildren().contains(child))
			return true;
		return false;
	}

	public void addChild(PComponent child) {
		this.getChildren().add(child);
		child.setParent(this);
		child.setVisual(this.getVisual());
	}

	public void removeChild(PComponent child) {
		this.getChildren().remove(child);
		child.setParent(null);
		this.setVisual(this.getVisual());
	}

	// Focus
	public boolean isFocused() {
		return this.getVisual().getFocusedComponent().equals(this);
	}

	public void requestFocus() {
		if (this.getVisual() == null)
			return;
		this.getVisual().setFocusedComponent(this);
	}

	// Lock
	public void mouseLock(boolean lock) {
		if (lock)
			getVisual().setMouseComponent(this);
		else
			getVisual().setMouseComponent(null);
	}

	public boolean mouseLocked() {
		return getVisual().getMouseComponent() == this;
	}

	// Mouse Events
	public PComponent findComponent(int x, int y) {
		for (int i = this.children.size() - 1; i >= 0; i--) {
			PComponent child = this.children.get(i);
			if (child.mouseInside(x - this.getLastX(), y - this.getLastY())) {
				return child;
			}
		}
		return null;
	}

	public void onMouseReleased(MouseEvent event) {
		PComponent component = findComponent(event.getX() - getParentLastX(), event.getY() - getParentLastY());
		if (component == null)
			return;

		component.onMouseReleased(event);
	}

	public void onMousePressed(MouseEvent event) {
		PComponent component = findComponent(event.getX() - getParentLastX(), event.getY() - getParentLastY());
		if (component == null)
			return;

		component.onMousePressed(event);
	}

	public void onMouseExited(MouseEvent event, MouseEvent lastEvent) {
		if (event == null)
			return;

		PComponent component = findComponent(event.getX() - getParentLastX(), event.getY() - getParentLastY());
		PComponent lastComponent = null;
		if (lastEvent != null) {
			lastComponent = findComponent(lastEvent.getX() - getParentLastX(), lastEvent.getY() - getParentLastY());

			if (lastComponent != null)
				lastComponent.onMouseExited(event, lastEvent);
		}

		if (component == null)
			return;

		component.onMouseExited(event, lastEvent);
	}

	public void onMouseEntered(MouseEvent event) {
		if (event == null)
			return;

		PComponent component = findComponent(event.getX() - getParentLastX(), event.getY() - getParentLastY());
		if (component == null)
			return;

		component.onMouseEntered(event);
	}

	public void onMouseClicked(MouseEvent event) {		
		PComponent component = findComponent(event.getX() - getParentLastX(), event.getY() - getParentLastY());
		if (component == null)
			return;

		component.onMouseClicked(event);
	}

	public void onMouseMoved(MouseEvent event, MouseEvent lastEvent) {
		PComponent component = findComponent(event.getX() - getParentLastX(), event.getY() - getParentLastY());
		PComponent lastComponent = null;
		if (lastEvent != null) {
			lastComponent = findComponent(lastEvent.getX() - getParentLastX(), lastEvent.getY() - getParentLastY());
		}

		if (component == null) {
			if (lastComponent != null)
				lastComponent.onMouseExited(event, lastEvent);
			lastComponent = null;
			return;
		}

		if (lastComponent != component) {
			if (lastComponent != null)
				lastComponent.onMouseExited(event, lastEvent);
			component.onMouseEntered(event);
		}

		component.onMouseMoved(event, lastEvent);
	}

	public void onMouseDragged(MouseEvent event, MouseEvent lastEvent) {
		PComponent component = findComponent(event.getX() - getParentLastX(), event.getY() - getParentLastY());
		PComponent lastComponent = null;
		if (lastEvent != null) {
			lastComponent = findComponent(lastEvent.getX() - getParentLastX(), lastEvent.getY() - getParentLastY());
		}

		if (component == null) {
			if (lastComponent != null)
				lastComponent.onMouseExited(event, lastEvent);
			lastComponent = null;
			return;
		}

		if (lastComponent != component) {
			if (lastComponent != null)
				lastComponent.onMouseExited(event, lastEvent);
			component.onMouseEntered(event);
		}

		component.onMouseDragged(event, lastEvent);
	}

	// Keyboard Events
	public void onKeyTyped(KeyEvent event) {
	}

	public void onKeyPressed(KeyEvent event) {
	}

	public void onKeyReleased(KeyEvent event) {
	}

	// Get & Set
	public Integer getLastX() {
		return lastX;
	}

	public void setLastX(Integer lastX) {
		this.lastX = lastX;
	}

	public Integer getLastY() {
		return lastY;
	}

	public void setLastY(Integer lastY) {
		this.lastY = lastY;
	}

	public Integer getLastWidth() {
		return lastWidth;
	}

	public void setLastWidth(Integer lastWidth) {
		this.lastWidth = lastWidth;
	}

	public Integer getLastHeight() {
		return lastHeight;
	}

	public void setLastHeight(Integer lastHeight) {
		this.lastHeight = lastHeight;
	}

	public Integer getParentLastX() {
		return parentLastX;
	}

	public void setParentLastX(Integer parentLastX) {
		this.parentLastX = parentLastX;
	}

	public Integer getParentLastY() {
		return parentLastY;
	}

	public void setParentLastY(Integer parentLastY) {
		this.parentLastY = parentLastY;
	}

	public PVisual getVisual() {
		return visual;
	}

	public void setVisual(PVisual visual) {
		this.visual = visual;
	}

	public PComponent getParent() {
		return parent;
	}

	public void setParent(PComponent parent) {
		this.parent = parent;
	}

	protected ArrayList<PComponent> getChildren() {
		return children;
	}

	protected void setChildren(ArrayList<PComponent> children) {
		this.children = children;
	}
}
